
syncvar.Set( "num_laps", 3 ) -- Number of laps per race. Used by GAMEMODE:GetNumLaps() in sh_hooks.lua

local MELON_PARTS = { "a", "b", "c" }
GM.ManhackSpawnDelay = 30
GM.SpawnDelay = 3 -- Spawn delay.
GM.EstMaxManhacks = 15 -- An estimate # of how many manhacks should be on the map.
GM.MinPlayers = 1
GM.Checkpoints = {} -- Contains all valid trigger_checkpoint entities in the map.
GM.PlayersFinished = {} -- Players who cross the finish line get added to this list.
GM.NextManhackRespawn = {}

-- Spawns all racers and freezes their melons. Call this before you call StartRace.
-- Not currently called from anywhere.
function GM:SpawnRacers()
	local racers = GAMEMODE:GetAllRacers()
	for i = 1, #racers do
		
		-- Spawn the racer and their melon
		racers[i]:Spawn()
		
		-- Freeze their melon
		local phys = racers[i]:GetMelon():GetPhysicsObject()
		phys:EnableMotion( false )
		phys:Sleep()
		
	end
end

-- Starts the race by unfreezing melons. Call this when... Meh you should know what to do with this.
-- Not currently called from anywhere.
function GM:StartRace()
	GAMEMODE.PlayersFinished = {}
	/*local racers = GAMEMODE:GetAllRacers()
	for i = 1, #racers do
		local phys = racers[i]:GetMelon():GetPhysicsObject()
		phys:EnableMotion( true )
		phys:Wake()
	end*/
	local ent = GAMEMODE.Checkpoints[1]
	gamemode.Call( "OnRaceStart" )
end

-- Sets up the Checkpoints table. Called from InitPostEntity.
function GM:SetUpCheckpoints()
	for k,v in pairs( ents.FindByClass( "trigger_checkpoint" ) ) do
		GAMEMODE.Checkpoints[ v.CheckpointID ] = v
	end
	syncvar.Set( "num_checkpoints", #GAMEMODE.Checkpoints ) -- Number of checkpoints in the map. Returned by GAMEMODE:GetNumCheckpoints() in sh_hooks.lua
end

-- Spawn and setup melon. Called from PlayerSpawn.
--[[function GM:SpawnMelon( ply )
	
	local melon = ply:GetMelon()
	if( not IsValid( ply:GetMelon() ) ) then
		melon = ents.Create( "player_melon" )
		melon:Spawn()
	end
	
	melon:SetPos( ply:GetPos() + ( vector_up * 16 ) )
	melon:SetAngles( ply:GetAngles() )
	
	ply:SetMelon( melon )
	ply:SetCheckpoint( 0 )
	
end]]

-- Gib explosion and whatnot. Called from PlayerDeath.
function GM:KillMelon( ply, attacker, inflictor )
	local melon = ply:GetMelon()
	if( not ValidEntity( melon ) ) then return end
	
	ply:SetPos( melon:GetPos() )
	
	for i = 1, 3 do
		local gib = ents.Create( "prop_physics" )
		gib:SetModel( "models/props_junk/watermelon01_chunk01"..MELON_PARTS[i]..".mdl" )
		gib:SetPos( melon:GetPos() )
		gib:Spawn()
		gib:Activate()
		gib:GetPhysicsObject():ApplyForceCenter( melon:GetVelocity() * 5 )
		timer.Simple( math.Rand( 5, 10 ), SafeRemoveEntity, gib )
	end
	
	SafeRemoveEntity( melon )
	
end

-- Used in PlayerCheckpoint.
function GM:CompareCheckpoints( iCheckpoint1, iCheckpoint2 )
	if( iCheckpoint1 == iCheckpoint2 ) then
		return 0
	elseif( iCheckpoint1 <= 1 and iCheckpoint2 == GAMEMODE:GetNumCheckpoints() ) then
		return 1
	elseif( iCheckpoint1 == GAMEMODE:GetNumCheckpoints() and iCheckpoint2 <= 1 ) then
		return -1
	elseif( iCheckpoint1 < iCheckpoint2 ) then
		return -1
	elseif( iCheckpoint1 > iCheckpoint2 ) then
		return 1
	end
end

-- Convenience function.
function GM:GetPreviousCheckpoint( iCheckpoint )
	if( iCheckpoint <= 1 ) then
		return GAMEMODE:GetNumCheckpoints()
	end
	return iCheckpoint - 1
end

-- Calls the OnPlayerCheckpoint hook. Called from trigger_checkpoint's StartTouch. Avoid editing this function unless you know what you're doing.
function GM:PlayerCheckpoint( ply, iCheckpoint )
	
	local iPlayerCheckpoint = ply:GetCheckpoint()
	local iDirection = GAMEMODE:CompareCheckpoints( iCheckpoint, iPlayerCheckpoint )
	
	/*local NextCheckpoint = 1
	if iCheckpoint+1 > GAMEMODE:GetNumCheckpoints() then
		ply:UpdateCheckpoint( 1 )
	else
		NextCheckpoint = iCheckpoint+1
		ply:UpdateCheckpoint( iCheckpoint+1 )
	end*/
	
	-- Debug
	if( iDirection == 0 ) then
		print( iCheckpoint .. " = " .. iPlayerCheckpoint )
	elseif( iDirection == 1 ) then
		print( iCheckpoint .. " > " .. iPlayerCheckpoint )
	elseif( iDirection == -1 ) then
		print( iCheckpoint .. " < " .. iPlayerCheckpoint )
	end
	
	if iDirection == 1 then
		GAMEMODE:SpawnManhacks( NextCheckpoint )
	end
	
	-- Player's LastValidCheckpoint is not nil, which means that last time this method ran the player went backwards through their current checkpoint.
	if( ply.m_iLastValidCheckpoint ) then
		
		-- See if they're going through their LastValidCheckpoint in the right direction.
		if( GAMEMODE:CompareCheckpoints( iCheckpoint, ply.m_iLastValidCheckpoint ) >= 0 and iDirection >= 0 ) then
			
			ply.m_iLastValidCheckpoint = nil
			
		end
		
		ply:SetCheckpoint( iCheckpoint )
		
		-- Regardless of which way they're going, we don't want any hooks to be called until they pass their next checkpoint.
		return
		
	-- Player went back through a previous checkpoint (or skipped backwards somehow).
	elseif( iDirection < 0 ) then
		
		-- Set their LastValidCheckpoint so this method can't call any hooks until they go through it again in the right direction.
		if( iPlayerCheckpoint == 0 ) then
			ply.m_iLastValidCheckpoint = 1
		else
			ply.m_iLastValidCheckpoint = iPlayerCheckpoint
		end
		
		ply:SetCheckpoint( GAMEMODE:GetPreviousCheckpoint( iPlayerCheckpoint ) )
		
		return
		
	elseif( iDirection == 0 ) then
		
		-- If you go through the same checkpoint twice don't do anything.
		return
		
	end
	
	if( iPlayerCheckpoint == 0 and iCheckpoint != 2 ) then return end
	
	ply:SetCheckpoint( iCheckpoint )
	
	gamemode.Call( "OnPlayerCheckpoint", ply, iCheckpoint )
	
	-- The player crossed the finish line and must be going the right way.
	if( iCheckpoint == 1 and iPlayerCheckpoint == GAMEMODE:GetNumCheckpoints() ) then
		
		GAMEMODE:PlayerLap( ply, ply:GetLap(), ply:GetCurrentTime() )
		
		-- This lap was their final lap
		if( ply:GetLap() > GAMEMODE:GetNumLaps() ) then
			
			GAMEMODE:PlayerFinish( ply )
			
		end
		
	end
	
end

-- Calls the OnPlayerLap hook. Called from PlayerCheckpoint. Avoid editing this function unless you know what you're doing.
function GM:PlayerLap( ply, iLapNumber, fTime )
	
	ply:SetSyncVar( "lap_start_time", CurTime() )
	
	if( iLapNumber <= GAMEMODE:GetNumLaps() ) then
		ply:SetLap( iLapNumber+1 )
	end
	
	if( ply:GetBestTime() > fTime ) then
		ply:SetBestTime( fTime )
	end
	
	gamemode.Call( "OnPlayerLap", ply, iLapNumber, fTime )
	
end

-- Calls the OnPlayerFinish, OnFirstPlayerFinish, and OnLastPlayerFinish hooks. Called from PlayerCheckpoint. Avoid editing this function unless you know what you're doing.
function GM:PlayerFinish( ply )
	
	table.insert( GAMEMODE.PlayersFinished, ply )
	ply:SetPlace( #GAMEMODE.PlayersFinished )
	
	gamemode.Call( "OnPlayerFinish", ply, ply:GetPlace() )
	
	if( #GAMEMODE.PlayersFinished == 0 ) then
		gamemode.Call( "OnFirstPlayerFinish", ply )
	elseif( #GAMEMODE.PlayersFinished == #GAMEMODE:GetAllRacers() ) then
		gamemode.Call( "OnLastPlayerFinish", ply )
	end
	
end
